package optimizationrunner;

import java.io.File;
import java.util.Arrays;
import java.util.Comparator;

import optimization.aco.AntColonyOptimizationAlgorithm;
import optimization.aco.config.AntColonyOptimizationConfig;
import optimizationrunner.config.OptimizationRunnerConfig;
import task.AbstractAutomatonTask;
import task.config.AbstractAutomatonTaskConfig;
import task.factory.TaskFactory;
import util.Pair;
import automaton.AutomatonMetaData;
import automaton.efsm.DefiniteEFSM;

/**
 * 
 * @author Daniil Chivilikhin
 *
 */
public class OptimizationRunner {
	private AbstractAutomatonTask task;
	private AntColonyOptimizationAlgorithm acoAlgorithm;
	private TaskFactory taskFactory;
	private OptimizationRunnerConfig config;
	
	public OptimizationRunner(OptimizationRunnerConfig config) {
		this.config = config;
		taskFactory = new TaskFactory(new AbstractAutomatonTaskConfig(config.taskConfigFileName()));
		task = taskFactory.createTask();
		acoAlgorithm = new AntColonyOptimizationAlgorithm(task, new AntColonyOptimizationConfig(config.acoConfigFileName()));
	}
	
	private DefiniteEFSM findBestMutatedDFA(DefiniteEFSM dfa) {
		double fitness = task.getFitness(dfa).getFitness();
		Pair<Double, DefiniteEFSM> mutatedAutomatas[] = new Pair[dfa.getNumberOfTransitions()];
		int transitionCounter = 0;
		for (int i = 0; i < dfa.getNumberOfStates(); i++) {
			for (int j = 0; j < dfa.getNumberOfExternalInfluences(); j++) {
				if (dfa.transitions[i][j].getEndState() == -1) { 
					continue;
				}
				DefiniteEFSM mutatedDFA = mutateDFA(dfa, i, j);
				double testFitness = task.getFitness(mutatedDFA).getFitness();
				mutatedAutomatas[transitionCounter++] = new Pair<Double, DefiniteEFSM>(testFitness, mutatedDFA);
			}
		}
		
		Arrays.sort(mutatedAutomatas, new Comparator<Pair<Double, DefiniteEFSM>>() {
			@Override
			public int compare(Pair<Double, DefiniteEFSM> arg0, Pair<Double, DefiniteEFSM> arg1) {
				if (arg0.first < arg1.first) {
					return 1;
				}
				return -1;
			}
		});
		
		if (mutatedAutomatas[0].first - fitness > 1e-5) {
			return mutatedAutomatas[0].second;
		} else {
			if (Math.abs(mutatedAutomatas[0].first - fitness) < 1e-5) {
				if (mutatedAutomatas[0].second.getNumberOfTransitions() < 
						dfa.getNumberOfTransitions()) {
					return mutatedAutomatas[0].second;
				}
			}
		}
		return dfa;
	}
	
	private DefiniteEFSM mutateDFA(DefiniteEFSM dfa, int state, int event) {
		DefiniteEFSM result = new DefiniteEFSM(dfa);
//		result.transitions[state][event] = new DFA.Transition(state, -1, "");
		return result;
	}
	
	public void run() {
		String attemptsDirName = config.solutionDirName();
		File attemptsDir = new File(attemptsDirName);
		attemptsDir.mkdir();
		
		for (int i = 0; i < config.numberOfExperiments(); i++) {
			String dirName = attemptsDirName + "/attempt" + i + "/";
			File dir = new File(dirName);
			dir.mkdir();
			
			AutomatonMetaData bestAutomata = acoAlgorithm.run();
			
			bestAutomata.printTransitionDiagram(dirName);
			bestAutomata.printToGraphViz(dirName, task.getEvents());
			bestAutomata.printMetaData(dirName);
		}
	}
}
